#include "cope.h"
#include "packet-pool.h"
#include "ip.h"
#include <iostream>
using namespace std;
PacketPool::PacketPool() {
    //    pkt_table_ = NULL;
    // b_expired_ = true;
    // b_first_ = true;
    // pre_pkt_ = 0;
    // next_pkt_ = 0;
}

PacketPool::~PacketPool() {
    // pkt_entry_t *current, *temp;
    // HASH_ITER(hh, pkt_table_, current, temp) {
    // 	HASH_DEL(pkt_table_, current);
    // 	free(current);
    // }

    // hash_map<int, Packet*>::iterator it = pkt_table_.begin();
    // for(; it != pkt_table_.end(); ++ it) {
	
    // }

    pkt_table_.clear();
    
    report_list_.clear();

    // should free the Packet* first??
    // should there be other iterator for temporary storage??
    pkt_iter_t it = backup_queue_.begin();
    for(; it != backup_queue_.end(); ++it) {
	Packet::free(it->ppkt);
    }
    //backup_queue_.clear();
}

/*
 * 1. check whether the packet has been in the pool
 * 2. fistly, add this packet into the backup-queue
 * 3. secondly, add this packet's info into the hash-table
 * 4. finally, add this packet's info into the reception report list
 */
void PacketPool::in_pool(Packet* pkt, COPE* cp) {

    // if(cp->node_->nodeid() == 1
    //    && (HDR_CMN(pkt)->uid_ == 11325))
    // 	cout << CUR_TIME << " node: "
    // 	     << cp->node_->nodeid() << " inpool"
    // 	     << " uid: " << HDR_CMN(pkt)->uid_
    // 	     << " copeid: " << HDR_CMN(pkt)->cope_pkt_id()
    // 	     << endl;
      
    // if it's an encoded pkt, just ignore it
    if((int)HDR_COPE(pkt)->encoded_num_ > 0)
	return;

    // 1. check whether the packet has been in the pool
    if(get_packet(HDR_CMN(pkt)->cope_pkt_id()) != NULL)
	return;
    // pkt_entry_t *result;
    // int x = HDR_CMN(pkt)->cope_pkt_id();
    // HASH_FIND_INT(pkt_table_, &x, result);
    // if(result != NULL) {
    // 	return;
    // }

    hdr_cmn* hc = HDR_CMN(pkt);
    u_int16_t ipseq = hc->cope_seq_num();
    //cout << "--------------inpool " << ipseq << " ";
    nsaddr_t srcip = HDR_IP(pkt)->saddr();
    //cout << srcip << endl;
    report_iter_t it = lookup_node(srcip);

    // incoming packet has been garbage collected, igonre it
    if(it != report_list_.end() && ipseq <= it->shifts)
	return;
 
    Packet * p = pkt->copy();
  
    // 2. fistly, add this packet into the backup-queue
    //    Packet* new_pkt = p->copy(); // don't forget to free it
    pkt_entry_t pt;
    pt.time = Scheduler::instance().clock();
    pt.ppkt = p;
    backup_queue_.push_back(pt);
  
    // 3. secondly, add this packet's info into the hash-table
    pkt_table_[hc->cope_pkt_id()] = p;
    // pkt_entry_t *new_entry = (pkt_entry_t*)malloc(sizeof(pkt_entry_t));
    // new_entry->pktid = hc->cope_pkt_id();
    // new_entry->ppkt = p;
    // HASH_ADD_INT(pkt_table_, pktid, new_entry);

 
    // 4. finally, add this packet's info into the reception report list

    // check whether this src_ip has been in the report list or not
    report_t rpt;
    // int pktid = hc->cope_pkt_id();
    if(it == report_list_.end()) { // not exist
	rpt.srcip = srcip;
	rpt.lastpkt = ipseq;
	// rpt.gc = false;
	rpt.shifts = 0;
	// rpt.pre = 0;
	// rpt.next = 0;
	rpt.max = 0;
	/*
	 * This ip seq number is different from local seq number, because
	 * the first received ip seq number of this node maybe is already
	 * bigger than 256, so we sould get the modulo of it.
	 */
	rpt.bitmap.set(BITMAP_TOTAL_SIZE - ((ipseq - 1) % BITMAP_TOTAL_SIZE) - 1);
	// if(b_expired_ == true) {
	//     pre_pkt_ = pktid;
	//     rpt.pre = ipseq;
	// } else {
	//     next_pkt_ = pktid;
	//     rpt.next = ipseq;
	// }
	report_list_.push_back(rpt);
    } else { // this node exists in the report list
	it->lastpkt = it->lastpkt > ipseq ? it->lastpkt : ipseq;

	// cout << "inpool: time: " << Scheduler::instance().clock()
	//      << " ipseq: " << (int)ipseq
	//      << " shifts: " << (int)it->shifts
	//      << " uid: " << HDR_CMN(pkt)->uid_
	//      << " node: " << cp->node_->nodeid()
	//      << endl;
	it->bitmap.set(BITMAP_TOTAL_SIZE - ((ipseq - it->shifts - 1) % BITMAP_TOTAL_SIZE) - 1);
	// if(b_expired_ == true) {
	//     it->pre = it->pre > ipseq ? it->pre : ipseq;
	//     pre_pkt_ = pktid;
	// } else {
	//     it->next = it->next > ipseq ? it->next : ipseq;
	//     next_pkt_ = pktid;
	// }
    }
}

void PacketPool::delete_pkt_ht(int pktid) {
    pkt_table_.erase(pktid);
    // pkt_entry_t* entry;
    // HASH_FIND_INT(pkt_table_, &pktid, entry);
    // if(entry != NULL) {
    // 	HASH_DEL(pkt_table_, entry);
    // 	free(entry);
    // }
}

Packet* PacketPool::get_packet(int pktid) {
    // hash_map<int, Packet*>::iterator iter = pkt_table_.begin();
    // cout << pktid << " ------------pool--------------" << endl;
    // for(; iter != pkt_table_.end(); ++iter) {
    // 	cout << "copeid: " << iter->first << endl;
    // }
    
    hash_map<int, Packet*>::iterator it;
    it = pkt_table_.find(pktid);
    if(it == pkt_table_.end())
	return NULL;
    return it->second;
    
    // pkt_entry_t* result;
    // HASH_FIND_INT(pkt_table_, &pktid, result);
    // if(result != NULL) 
    // 	return result->ppkt;
    // return NULL;
}

/*
 * 1. first delete the report-list part, if has not done before
 * 2. second delete the hash-table part
 * 3. finally delete the backup-queue part
 * 4. reset the flags
 */
void PacketPool::garbage_collection(COPE* cp) {

    // cout << cp->node_->nodeid() << "----gc----" << Scheduler::instance().clock() << endl;
    if(backup_queue_.empty()) {
	//	b_expired_ = !b_expired_;
	return;
    }

    // if(pre_pkt_ == 0 || next_pkt_ == 0) {
    // 	b_expired_ = !b_expired_;
    // 	b_first_ = false;
    // 	return;
    // }
    
    Packet* tmp;
    int id;
    // int lastone;
    // if(b_expired_ == true)
    // 	lastone = pre_pkt_;
    // else
    // 	lastone = next_pkt_;

    pkt_iter_t it_bq = backup_queue_.begin();
    // for( ; it_bq != backup_queue_.end(); it_bq ++) {
    // 	printf(" <<<packet-id: %d, lastone: %d\n", HDR_CMN(*it_bq)->cope_pkt_id(), lastone);
    // }
    // it_bq = backup_queue_.begin();
    double t = Scheduler::instance().clock();
    
    while(it_bq != backup_queue_.end() && it_bq->time < (t - cp->gc_interval_)) {
	tmp = it_bq->ppkt;
	hdr_cmn* hc = HDR_CMN(tmp);
	id = hc->cope_pkt_id();
	u_int16_t ipseq = hc->cope_seq_num();
	
	// 1. first delete the report-list part, if has not done before
	nsaddr_t srcip = HDR_IP(tmp)->saddr();
	report_iter_t it = lookup_node(srcip);

	if(it->max < ipseq)
	    it->max = ipseq;
	if(it->max > it->shifts) {
	    it->bitmap <<= (it->max - it->shifts) % BITMAP_TOTAL_SIZE;
	    it->shifts += (it->max - it->shifts) % BITMAP_TOTAL_SIZE;
	}
	

	// 2. second delete the hash-table part
	delete_pkt_ht(id);
	// if(cp->node_->nodeid() == 1
	//    && (HDR_CMN(*it_bq)->uid_ == 11325))
	//     cout << CUR_TIME << " node: "
	// 	 << cp->node_->nodeid() << " gc"
	// 	 << " uid: " << HDR_CMN(*it_bq)->uid_
	// 	 << " copeid: " << id
	// 	 << endl;


	// 3. finally delete the backup-queue part
	Packet::free(it_bq->ppkt);
	it_bq = backup_queue_.erase(it_bq);

	// if(id == lastone)
	//     break;
    }

    // 4.1 reset the expired flag
    // b_expired_ = !b_expired_;

    // 4.2 reset each node's gc flag
    // report_iter_t it = report_list_.begin();
    // for(; it != report_list_.end(); ++it) {
    // 	it->gc = false;
    // }
}

report_iter_t PacketPool::lookup_node(nsaddr_t srcip) {
    report_iter_t it = report_list_.begin();
    for(; it != report_list_.end(); ++it) {
	if(it->srcip == srcip) {
	    return it;
	}
    }
    return it;
}

/*
 * 1. if that node does not exist in the pool, return NULL
 * 2. get the report
 */
hdr_report_t* PacketPool::get_report(nsaddr_t srcip) {
    // check whether this node does exist in the pool
    report_iter_t it = lookup_node(srcip);
    if(it == report_list_.end()) {
	return NULL;
    }
    
    hdr_report_t* rpt = (hdr_report_t*)malloc(sizeof(hdr_report_t));

    rpt->src_ip_ = srcip;
    rpt->last_pkt_ = it->lastpkt;
    //u_int8_t x = it->shifts;
    u_int8_t lastpkt_mod = it->lastpkt % BITMAP_TOTAL_SIZE;
    u_int8_t shifts_mod = it->shifts % BITMAP_TOTAL_SIZE;
    // cout << "pool-last: " << (int)lastpkt_mod
    // 	 << " pool-shift: " << (int)shifts_mod<<endl;
    // if the lastpkt_mod < shifts_mod, then lastpkt > 256, while shifts < 256
    if(lastpkt_mod < shifts_mod) {
	if(lastpkt_mod == 0) {
	    bitset<BITMAP_SIZE> temp_map(it->bitmap.to_string(),
					 BITMAP_TOTAL_SIZE - BITMAP_SIZE - 1,
					 BITMAP_SIZE);
	    rpt->bit_map_ = temp_map.to_ulong();	    
	} else if(lastpkt_mod > 0 && lastpkt_mod <= BITMAP_SIZE) {
	    // the bitmap has two (three) parts

	    u_int8_t end_num = BITMAP_SIZE + 1 - lastpkt_mod;
	    // get the end part
	    bitset<BITMAP_SIZE> temp_end(it->bitmap.to_string(),
					 BITMAP_TOTAL_SIZE - end_num,
					 end_num);
	    // shifts lastpkt_mod - 1 bits to head part
	    temp_end <<= lastpkt_mod - 1;

	    // get the head part
	    bitset<BITMAP_SIZE> temp_head(it->bitmap.to_string(),
					  0,
					  lastpkt_mod - 1);
	    // get the OR of two bitmap
	    temp_end |= temp_head;
	    rpt->bit_map_ = temp_end.to_ulong();
	} else { // get the latest bits
	    bitset<BITMAP_SIZE> temp_map(it->bitmap.to_string(),
					 lastpkt_mod - BITMAP_SIZE - 1,
					 BITMAP_SIZE);
	    rpt->bit_map_ = temp_map.to_ulong();
	}
    } else { // shifts_mod < lastpkt_mod < 256
	if(lastpkt_mod - shifts_mod <= BITMAP_SIZE) { //y bitmap has two parts
	    if(lastpkt_mod - shifts_mod - 1 == 0)
		rpt->bit_map_ = 0;
	    else {
		bitset<BITMAP_SIZE> temp_map(it->bitmap.to_string(),
					     0,
					     lastpkt_mod - shifts_mod - 1);
		rpt->bit_map_ = temp_map.to_ulong();
	    }
	} else { // get the latest bits
	    bitset<BITMAP_SIZE> temp_map(it->bitmap.to_string(),
					 lastpkt_mod - BITMAP_SIZE - shifts_mod - 1,

					 BITMAP_SIZE);
	    rpt->bit_map_ = temp_map.to_ulong();
	}
    }

    
    // cout << it->bitmap.to_string() << endl; 
    // cout << "Report : " << bitset<8>(rpt->bit_map_)
    // 	 << " srcip: " << rpt->src_ip_
    // 	 << " lastpkt: " << rpt->last_pkt_ << endl;
    //    showPool();

    return rpt;
}
